﻿using System;
using System.Linq;
using GammaJul.ReSharper.GammaXaml.Psi.Xaml;
using JetBrains.Annotations;
using JetBrains.Application;
using JetBrains.ReSharper.Daemon.UsageChecking;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Xaml.DeclaredElements;
using JetBrains.ReSharper.Psi.Xaml.Impl.Util;
using JetBrains.ReSharper.Psi.Xaml.Tree;

namespace GammaJul.ReSharper.GammaXaml.Analysis.Xaml {

	/// <summary>
	/// XAML named elements bound to a Caliburn method are marked as used implicitly.
	/// </summary>
	[ShellComponent]
	public class CaliburnUsageInspectionsSuppressor : IUsageInspectionsSupressor {

		public bool SupressUsageInspectionsOnElement(IDeclaredElement element, out ImplicitUseKindFlags flags) {
			flags = ImplicitUseKindFlags.Default;
			
			var xamlField = element as IXamlField;
			if (xamlField != null)
				return IsXamlFieldCaliburnBound(element);

			var @class = element as IClass;
			if (@class != null)
				return IsXamlViewBoundCaliburnBound(@class);

			return false;
		}

		private static bool IsXamlFieldCaliburnBound(IDeclaredElement element) {
			foreach (var declaration in element.GetDeclarations().OfType<IXamlObjectElementDeclaration<IDeclarationNameAttribute>>()) {
				var nameAttribute = declaration.GetDeclarationNameElement();
				if (nameAttribute == null)
					continue;

				var reference = nameAttribute.FindReference<CaliburnBoundXamlReference>();
				if (reference != null && reference.Resolve().DeclaredElement != null)
					return true;

			}
			return false;
		}

		private static bool IsXamlViewBoundCaliburnBound(IClass @class) {
			foreach (var declaration in @class.GetDeclarations().OfType<IXamlTypeDeclaration>()) {
				if (!declaration.GetProject().IsCaliburnMicroProject())
					return false;

				IXamlObjectElement element;
				var vmType = BindingUtil.CalculateSourceType(declaration.FirstChild, out element) as IDeclaredType;
				if (vmType == null || vmType.IsUnknown)
					continue;

				if (AreNamespacesMatching(vmType.GetClrName(), @class.GetClrName()))
					return true;

			}
			return false;
		}

		private static bool AreNamespacesMatching([NotNull] IClrTypeName viewModel, [NotNull] IClrTypeName view) {
			if (!view.ShortName.EndsWith("View", StringComparison.Ordinal)
			|| !viewModel.ShortName.EndsWith("ViewModel", StringComparison.Ordinal)
			|| viewModel.ShortName != view.ShortName + "Model")
				return false;

			string vmNamespace = viewModel.GetNamespaceName();
			string viewNamespace = view.GetNamespaceName();

			return vmNamespace == viewNamespace
				|| (vmNamespace.EndsWith(".ViewModels", StringComparison.Ordinal) && viewNamespace.EndsWith(".Views", StringComparison.Ordinal));
		}

	}

}